
class LineCache:
    def __init__(self, file_, line_width, accelerator=None):
        self.line_length = -1
        self.file = file_

        self.accelerator = accelerator or list()

        self.accelerator_nb = len(accelerator)
        self.reset(line_width)

    def __len__(self):
        return self.accelerator_nb

    def __getitem__(self, k):
        if k < self.accelerator_nb:
            return self.accelerator[k]
        else:
            return 0

    def reset(self, line_width):
        if line_width != self.line_length:
            self.line = dict()
            self.line_length = line_width
        self.template = '%%08x%%-%ds %%s' % (3 * line_width)

    def getContentWithLine(self, line_nb):
        if line_nb not in self.line:
            self.update(line_nb)

        return self.line.get(line_nb, '').replace('\t', ' ')

    def update(self, line_nb):
        if self.accelerator_nb > 0:
            if line_nb + 1 < self.accelerator_nb:
                offset = self.accelerator[line_nb]
                length = self.accelerator[line_nb + 1] - offset
                self.line[line_nb] = \
                    self.file.read(length, offset).rstrip('\r\n')
            else:
                self.line[line_nb] = ''
        else:
            offset = self.line_length * line_nb
            dat = self.file.read(self.line_length, offset)
            if dat:
                cx, dx = '', ''
                for c in dat:
                    d = ord(c)
                    cx += ' %02x' % d
                    if 0x20 <= d < 0x7f:
                        dx += c
                    else:
                        dx += '.'

                self.line[line_nb] = self.template % (offset, cx, dx)

    def getLength(self, line_nb=0):
        if self.accelerator_nb > 0:
            return len(self.line.get(line_nb, ''))
        else:
            return 8 + self.line_length * 4

    def getLineNumWithOffset(self, offset):
        for line, length in enumerate(self.accelerator):
            if length >= offset:
                return line

        return 0

    def getMaxLength(self):
        max_val = self.line_length

        if self.accelerator_nb > 0:
            max_val = max(max_val, self.accelerator[0])

        k = 0
        while k + 1 < self.accelerator_nb:
            delta = self.accelerator[k + 1] - self.accelerator[k]
            if delta > max_val:
                max_val = delta

            k += 1

        return max(max_val, self.getLength())

    def getSp(self):
        return 8, 8 + self.line_length * 3

    def getOffset(self, offset, length, start=0):
        a, b = self.getSp()

        xa, xb = (a + 1 + 3 * offset, b + 1 + offset)
        la, lb = (3 * length - 1, length)

        if xa < start:
            la -= start - xa
            xa = 0
        else:
            xa -= start

        if xb < start:
            lb -= start - xb
            xb = 0
        else:
            xb -= start

        return (a > 0, xa, la, start < b, xb, lb)

    def getCharOffset(self, pos):
        ret = 0

        a, b = self.getSp()
        if pos > b:
            ret = pos - b
        elif pos > a:
            ret = (pos - a) / 3
        else:
            ret = -1

        return ret
